home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume6 / bsearchstr < prev    next >
Encoding:
Internet Message Format  |  1986-11-30  |  10.6 KB

  1. Subject: v06i050:  Binary search for strings in a file (bsearchstr)
  2. Newsgroups: mod.sources
  3. Approved: rs@mirror.UUCP
  4.  
  5. Submitted by: hplabs!hpfcla!ajs
  6. Mod.sources: Volume 6, Issue 50
  7. Archive-name: bsearchstr
  8.  
  9. [  I compiled and tested this on a 4.2BSD Vax750 and had no problems.
  10.    The README is right -- just do a "vanilla cc"; add -DDEBUG to
  11.    get a test stub with main.  --r$ ]
  12.  
  13. #!/bin/sh
  14. # This is a shell archive.  Remove anything before this line,
  15. # then unpack it by saving it in a file and typing "sh file".
  16. #
  17. # Wrapped by mirror!rs on Fri Jul 11 15:00:17 EDT 1986
  18. # Contents:  README bsearchstr.3 bsearchstr.c
  19.  
  20. echo x - README
  21. sed 's/^XX//' > "README" <<'@//E*O*F README//'
  22. XXInformation on bsearchstr(3):
  23.  
  24. XX1.  This library routine is like bsearch(3), but generalized to work
  25. XX    with files (not memory images) of random length strings (not fixed
  26. XX    length objects).
  27.  
  28. XX2.  Unlike bsearch(), it is well-behaved.  If more than one data item
  29. XX    (text line) matches the pattern, it returns the first matching item,
  30. XX    not a random one.
  31.  
  32. XX3.  It's been around a while, well tested, and shown itself to be
  33. XX    generally useful, mainly for lookups into large, sorted lists.
  34.  
  35. XX4.  It runs on HP-UX (AT&T V.2); hasn't been tested on other variants.
  36.  
  37. XX5.  No makefile provided or needed -- compilation and linking are
  38. XX    vanilla.
  39.  
  40. XX6.  No warranty express or implied accompanies this software.  Caveat
  41. XX    emptor.  See the manual entry.
  42.  
  43. XXAlan Silverstein, Hewlett-Packard Systems Software Operation, Fort Collins,
  44. XXColorado; {ihnp4 | hplabs}!hpfcla!ajs; 303-229-3053; (lat-long on request :-)
  45. @//E*O*F README//
  46. chmod u=rw,g=rw,o=rw README
  47.  
  48. echo x - bsearchstr.3
  49. sed 's/^XX//' > "bsearchstr.3" <<'@//E*O*F bsearchstr.3//'
  50. XX.TH BSEARCHSTR 3 "HP Experimental"
  51. XX.SH NAME
  52. XXbsearchstr \- binary search a sorted file of random-length strings
  53. XX.SH SYNOPSIS
  54. XX.B "long bsearchstr (stream, pattern, caseins, tellnext)"
  55. XX.br
  56. XX.B "FILE\0\(**stream;"
  57. XX.br
  58. XX.B "char\0\(**pattern;"
  59. XX.br
  60. XX.B "int\0\0caseins;"
  61. XX.br
  62. XX.B "int\0\0tellnext;"
  63. XX.ad b
  64. XX.SH HP-UX COMPATIBILITY
  65. XX.TP 10
  66. XXLevel:
  67. XXHP-UX/RUN ONLY
  68. XX.TP
  69. XXOrigin:
  70. XXHewlett-Packard
  71. XX.SH DESCRIPTION
  72. XX.B Bsearchstr
  73. XXis a binary search routine which understands ordinary files containing sorted,
  74. XXrandom-length strings (lines) terminated by newline characters.
  75. XXIt returns the byte offset of the first line in the file which begins with the
  76. XXgiven pattern, taken as a simple string of characters (regular expressions are
  77. XXnot supported).
  78. XXIt also sets the file location to the line beginning at the offset returned,
  79. XXusing \fIfseek\fR\^(3s).
  80. XX.PP
  81. XX\fIStream\fR must be a currently open file containing lines previously sorted
  82. XXin increasing order by the simple numeric values of the characters.
  83. XX.PP
  84. XX\fIPattern\fR must be a valid pointer to char.
  85. XXA nil pattern (e.g. \(**pattern == 0) matches anything, so
  86. XX.B bsearchstr
  87. XXreturns zero (start of file) in this case.
  88. XXA nil line in the file (e.g. nothing but a newline) is treated as being less
  89. XXthan any non-nil pattern.
  90. XX.PP
  91. XXIf
  92. XX.I caseins
  93. XXis zero, searching is done case-sensitively, and the data file should be sorted
  94. XXaccordingly.
  95. XXIf
  96. XX.I caseins
  97. XXis non-zero, searching is done case-insensitively, and the data must have been
  98. XXsorted using "sort \-f" or equivalent.
  99. XXThe routine uses
  100. XX.IR malloc (3)
  101. XXto get memory to make an uppercased copy of
  102. XX.IR pattern ,
  103. XXso as not to modify the passed-in value.
  104. XX.PP
  105. XXIf \fItellnext\fR is zero, and no line in the file begins with \fIpattern\fR,
  106. XX.B bsearchstr
  107. XXreturns \-1.
  108. XXIf \fItellnext\fR is non-zero, it returns the offset of the first line
  109. XXlexically after \fIpattern\fR.
  110. XXIt returns the size of the file if the last line is before \fIpattern\fR.
  111. XX.PP
  112. XXEach time
  113. XX.B bsearchstr
  114. XXhalves the remainder of the file, it seeks to a
  115. XXlocation which is usually not the start of a line.
  116. XXIt searches forwards, then backwards, a character at a time, to locate the
  117. XXnext start of line.
  118. XXThis is normally more efficient than linear searching the file, especially if
  119. XXthe file is large and no lines are extremely long.
  120. XXAlso,
  121. XX.B bsearchstr
  122. XXworks fine even if the last line in the file is not
  123. XXterminated by a newline.
  124. XX.SH SEE ALSO
  125. XXsort(1), bsearch(3c), fgets(3s), fopen(3s), hsearch(3c), lsearch(3c),
  126. XXqsort(3c), tsearch(3c).
  127. XX.SH DIAGNOSTICS
  128. XXReturns \-1 if no line matches and \fItellnext\fR is zero.
  129. XX.PP
  130. XXReturns \-2 if any
  131. XX.IR fseek (3s),
  132. XX.IR ftell (3s),
  133. XXor
  134. XX.IR malloc (3c)
  135. XXerror occurs.
  136. XXIn this case the current file location is unpredictable.
  137. @//E*O*F bsearchstr.3//
  138. chmod u=rw,g=rw,o=rw bsearchstr.3
  139.  
  140. echo x - bsearchstr.c
  141. sed 's/^XX//' > "bsearchstr.c" <<'@//E*O*F bsearchstr.c//'
  142. XXstatic char Uni_id[] = "@(#)28.1";
  143. XX/* UNISRC_ID: @(#)bsearchstr.c    28.1    86/01/04  */
  144. XX/*
  145. XX * Binary search routine for sorted file of strings (lines).
  146. XX * Compile with -DDEBUG for standalone testing and extra output.
  147. XX */
  148.  
  149. XX#include <stdio.h>
  150. XX#include <ctype.h>
  151.  
  152. XX#define    chNull    ('\0')
  153. XX#define    cpNull    ((char *) NULL)
  154.  
  155.  
  156. XX#ifdef DEBUG
  157. XX/*
  158. XX * Test usage:  cmd file pattern [caseins [tellnext]]
  159. XX *
  160. XX * If there is a third  argument, sends caseins  == 1.
  161. XX * If there is a fourth argument, sends tellnext == 1.
  162. XX */
  163.  
  164. XXmain (argc, argv)
  165. XX    int    argc;
  166. XX    char    **argv;
  167. XX{
  168. XX    FILE    *fp;
  169. XX    char    buf[BUFSIZ];
  170.  
  171. XX    fp = fopen (argv[1], "r");
  172. XX    printf ("==min=== ==max=== ==pos1== ==pos2== cmp match\n");
  173. XX    printf ("Search returns %ld\n",
  174. XX        bsearchstr (fp, argv[2], (argc > 3), (argc > 4)));
  175. XX    printf ("%s\n", fgets (buf, BUFSIZ, fp));
  176. XX}
  177. XX#endif /* DEBUG */
  178.  
  179.  
  180. XX/***************************************************************************
  181. XX * B S E A R C H S T R
  182. XX *
  183. XX * Binary search a stream consisting of sorted lines of data for the first
  184. XX * line that starts with the given pattern, or the next line if none and
  185. XX * tellnext == 1.  Set the file location to the first byte of that line
  186. XX * and return the offset.
  187. XX *
  188. XX * When caseins is set, takes pains not to modify the memory pointed to by
  189. XX * pattern.  Uses multi-statement macros for efficiency.
  190. XX *
  191. XX * See bsearchstr(3) for more information.
  192. XX *
  193. XX * Author:  Alan Silverstein, Hewlett-Packard Fort Collins Systems Division
  194. XX */
  195.  
  196. XX#define    LT 0
  197. XX#define    EQ 1
  198. XX#define    GT 2
  199.  
  200. XX#define    RETURN(value)    { if (caseins) free (pattern); return (value); }
  201. XX#define    SETPOS        { if (fseek (filep, pos, 0) < 0) RETURN (-2L); }
  202.  
  203. XXlong bsearchstr (filep, pattern, caseins, tellnext)
  204. XX    FILE    *filep;            /* file to search       */
  205. XX    char    *pattern;        /* start of line to match  */
  206. XX    int    caseins;        /* case insensitive?       */
  207. XX    int    tellnext;        /* tell next if not found? */
  208. XX{
  209. XX    /* start of first line not yet compared (lower limit) */
  210. XX    long    min = 0;
  211. XX    /* start of line after last line not yet compared (upper limit) */
  212. XX    long    max;
  213.  
  214. XX     long    pos;            /* current offset in file  */
  215. XX     char    *patp;            /* pattern pointer       */
  216. XX     /* warning: this must be an int, not a char */
  217. XXregister int    ch;            /* current char to compare */
  218. XXregister int    cmp;            /* results of comparison   */
  219. XX     int    match = 0;        /* true if match found       */
  220.  
  221. XX     char    *malloc();
  222.  
  223. XX/*
  224. XX * PREPARE PATTERN FOR CASE-INSENSITIVE SEARCH:
  225. XX *
  226. XX * Use toupper(), not tolower(), to be compatible with the actual function
  227. XX * of sort -f.
  228. XX */
  229.  
  230. XX    if (caseins)
  231. XX    {
  232. XX        int      len = strlen (pattern) + 1;    /* chars to uppercase    */
  233. XX        char  *cp;                /* for copying data    */
  234. XX        char  *cpend;            /* end of copy range    */
  235.  
  236. XX        if ((cp = patp = malloc (len)) == cpNull)
  237. XX        return (-2L);
  238.  
  239. XX        /* now remember to free (pattern) before returning    */
  240. XX        /* can use the RETURN() macro to accomplish this    */
  241.  
  242. XX        cpend = cp + len;
  243.  
  244. XX        while (cp < cpend)            /* copy chNull also         */
  245. XX        *cp++ = toupper (*pattern++);    /* toupper() is not a macro! */
  246.  
  247. XX        pattern = patp;            /* "move" to new location */
  248. XX    }
  249.  
  250. XX/*
  251. XX * SET MAX TO END OF FILE (byte past last) by seeking there:
  252. XX */
  253.  
  254. XX    if ((fseek (filep, 0L, 2) < 0) || ((max = ftell (filep)) < 0))
  255. XX        RETURN (-2L);
  256.  
  257. XX/*
  258. XX * SET NEXT STARTING POSITION (where to find string and compare):
  259. XX */
  260.  
  261. XX    while (min < max)            /* do until closure happens */
  262. XX    {
  263. XX        pos = (min + max) / 2;
  264. XX        SETPOS;
  265.  
  266. XX#ifdef DEBUG
  267. XX        printf ("%8ld %8ld %8ld ", min, max, pos);
  268. XX#endif
  269.  
  270. XX/*
  271. XX * LOOK FOR THE NEXT END OF LINE IN THE FORWARD DIRECTION:
  272. XX */
  273.  
  274. XX        while ((pos < max) && (getc (filep) != '\n'))
  275. XX        pos++;
  276.  
  277. XX        pos++;                /* skip newline */
  278.  
  279. XX/*
  280. XX * If we ran into max, we are nearly done, assuming the current last line is
  281. XX * not really huge compared to all the others (but this will work out OK too).
  282. XX * Simply reset pos = min and check the first current line.
  283. XX */
  284.  
  285. XX        if (pos >= max)
  286. XX        pos = min;
  287.  
  288. XX/*
  289. XX * COMPARE THE CURRENT LINE TO THE PATTERN:
  290. XX *
  291. XX * If pattern[0] == chNull, never does the for loop, so it matches anything.
  292. XX */
  293.  
  294. XX        SETPOS;
  295.  
  296. XX        for (cmp = EQ, patp = pattern; (*patp != chNull); patp++)
  297. XX        {
  298. XX        ch = caseins ? toupper (getc (filep)) : getc (filep);
  299.  
  300. XX        if ((ch < *patp) || (ch == '\n') || (ch == EOF))
  301. XX        {                /* line < pattern */
  302. XX            cmp = LT;
  303. XX            break;
  304. XX        }
  305. XX        else if (ch > *patp)        /* line > pattern */
  306. XX        {
  307. XX            cmp = GT;
  308. XX            break;
  309. XX        }
  310. XX        }
  311.  
  312. XX        match += (cmp == EQ);        /* note a match */
  313.  
  314. XX#ifdef DEBUG
  315. XX        printf ("%8ld  %c  %5d\n", pos,
  316. XX            ((cmp == LT) ? '<' : ((cmp == EQ) ? '=' : '>')), match);
  317. XX#endif
  318. XX        
  319. XX/*
  320. XX * MOVE MIN OR MAX according to the results of comparison:
  321. XX *
  322. XX * If pattern[0] == chNull, cmp == EQ, which is "safe".
  323. XX */
  324.  
  325. XX        if (cmp == LT)                /* min => next line */
  326. XX        {
  327. XX        min = pos + (patp - pattern);
  328.  
  329. XX        while ((ch != '\n') && (min < max))    /* find end */
  330. XX        {
  331. XX            ch = getc (filep);
  332. XX            min++;
  333. XX        }
  334. XX        min++;                    /* skip newline */
  335. XX        }
  336. XX        else                    /* max => this line */
  337. XX        {
  338. XX        max = pos;
  339. XX        }
  340.  
  341. XX    } /* while */
  342.  
  343. XX/*
  344. XX * FINISH UP:
  345. XX *
  346. XX * Note that min could be one too large if the last line is unterminated
  347. XX * and less than pattern, so we use max instead.
  348. XX */
  349.  
  350. XX    if (match || tellnext)            /* success */
  351. XX    {
  352. XX        pos = max;
  353. XX        SETPOS;
  354. XX        RETURN (pos);
  355. XX    }
  356. XX    else                    /* failure */
  357. XX    {
  358. XX        RETURN (-1L);
  359. XX    }
  360.  
  361. XX} /* bsearchstr */
  362. @//E*O*F bsearchstr.c//
  363. chmod u=rw,g=rw,o=rw bsearchstr.c
  364.  
  365. echo Inspecting for damage in transit...
  366. temp=/tmp/sharin$$; dtemp=/tmp/sharout$$
  367. trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
  368. cat > $temp <<\!!!
  369.       23     133     905 README
  370.       87     435    2678 bsearchstr.3
  371.      220     923    5190 bsearchstr.c
  372.      330    1491    8773 total
  373. !!!
  374. wc  README bsearchstr.3 bsearchstr.c | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
  375. if test -s $dtemp
  376. then echo "Ouch [diff of wc output]:" ; cat $dtemp
  377. else echo "No problems found."
  378. fi
  379. exit 0
  380.